package eval

import (
	"fmt"
	"testing"
	"showen/obj"
)

func TestSymbolToString(t *testing.T) {
	env := obj.NewEnvironment()

	// Test with a symbol
	sym := &obj.SymbolObject{Value: "hello"}
	result := SymbolToString([]obj.Object{sym}, env)
	if result.Type() != obj.STRING_OBJ {
		t.Errorf("Expected string result, got %s", fmt.Sprintf("%d", result.Type()))
	}
	if result.(*obj.StringObject).Value != "hello" {
		t.Errorf("Expected 'hello', got '%s'", result.(*obj.StringObject).Value)
	}

	// Test with a non-symbol
	str := &obj.StringObject{Value: "test"}
	result = SymbolToString([]obj.Object{str}, env)
	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("Expected error for non-symbol, got %s", fmt.Sprintf("%d", result.Type()))
	}
	expected := "symbol->string argument must be a symbol"
	if result.(*Error).Message != expected {
		t.Errorf("Expected error message '%s', got '%s'", expected, result.(*Error).Message)
	}

	// Test with wrong number of arguments
	result = SymbolToString([]obj.Object{}, env)
	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("Expected error for no arguments, got %s", fmt.Sprintf("%d", result.Type()))
	}
	expected = "symbol->string requires exactly 1 argument"
	if result.(*Error).Message != expected {
		t.Errorf("Expected error message '%s', got '%s'", expected, result.(*Error).Message)
	}
}

func TestStringToSymbol(t *testing.T) {
	env := obj.NewEnvironment()

	// Test with a string
	str := &obj.StringObject{Value: "world"}
	result := StringToSymbol([]obj.Object{str}, env)
	if result.Type() != obj.SYMBOL_OBJ {
		t.Errorf("Expected symbol result, got %s", fmt.Sprintf("%d", result.Type()))
	}
	if result.(*obj.SymbolObject).Value != "world" {
		t.Errorf("Expected 'world', got '%s'", result.(*obj.SymbolObject).Value)
	}

	// Test with a non-string
	sym := &obj.SymbolObject{Value: "test"}
	result = StringToSymbol([]obj.Object{sym}, env)
	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("Expected error for non-string, got %s", fmt.Sprintf("%d", result.Type()))
	}
	expected := "string->symbol argument must be a string"
	if result.(*Error).Message != expected {
		t.Errorf("Expected error message '%s', got '%s'", expected, result.(*Error).Message)
	}

	// Test with wrong number of arguments
	result = StringToSymbol([]obj.Object{}, env)
	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("Expected error for no arguments, got %s", fmt.Sprintf("%d", result.Type()))
	}
	expected = "string->symbol requires exactly 1 argument"
	if result.(*Error).Message != expected {
		t.Errorf("Expected error message '%s', got '%s'", expected, result.(*Error).Message)
	}
}

func TestSymbolIntegration(t *testing.T) {
	// Test the integration of all three procedures
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	// Test that symbol? works with string->symbol
	str := &obj.StringObject{Value: "test-symbol"}
	sym := StringToSymbol([]obj.Object{str}, env)
	if sym.Type() != obj.SYMBOL_OBJ {
		t.Fatalf("Expected symbol from string->symbol, got %s", fmt.Sprintf("%d", sym.Type()))
	}

	result := IsSymbolProc([]obj.Object{sym}, env)
	if result.Type() != obj.BOOLEAN_OBJ {
		t.Errorf("Expected boolean result, got %s", fmt.Sprintf("%d", result.Type()))
	}
	if !result.(*obj.BooleanObject).Value {
		t.Errorf("Expected true for symbol, got false")
	}

	// Test that symbol->string works with string->symbol
	backToStr := SymbolToString([]obj.Object{sym}, env)
	if backToStr.Type() != obj.STRING_OBJ {
		t.Errorf("Expected string from symbol->string, got %s", fmt.Sprintf("%d", backToStr.Type()))
	}
	if backToStr.(*obj.StringObject).Value != "test-symbol" {
		t.Errorf("Expected 'test-symbol', got '%s'", backToStr.(*obj.StringObject).Value)
	}

	// Test round-trip: string->symbol->string
	if str.Value != backToStr.(*obj.StringObject).Value {
		t.Errorf("Expected round-trip conversion to preserve value. Started with '%s', ended with '%s'", 
			str.Value, backToStr.(*obj.StringObject).Value)
	}
}